# Copyright (c) 2010, Ryan Bourgeois <bluedragonx@gmail.com>
# All rights reserved.
#
# This software is licensed under a modified BSD license as defined in the
# provided license file at the root of this project.  You may modify and/or
# distribute in accordance with those terms.
#
# This software is provided "as is" and any express or implied warranties,
# including, but not limited to, the implied warranties of merchantability and
# fitness for a particular purpose are disclaimed.

import re
from items import Atom, UseFlag, Keyword
from util import Tabber

comment_re = re.compile('#.*')
whitespace_re = re.compile('\s+')

def _findpkg(packages, atom):
    for i in range(len(packages)):
        if packages[i][0].match(atom):
            return i
    return None

def _cmppkg(lt, rt):
    lt = lt[0]
    rt = rt[0]
    fmt = '%s/%s-%s'
    ltstr = (fmt % (lt.category, lt.name, lt.version)).lower()
    rtstr = (fmt % (rt.category, rt.name, rt.version)).lower()
    return cmp(ltstr, rtstr)

def _sortpkgs(packages):
    return sorted(packages, _cmppkg)

class Use:

    def __init__(self, file=None):
        self.packages = []
        self.file = file
        if file is not None:
            self._readfile(file)

    def _readfile(self, file):
        fd = open(file, 'r')
        try:
            for line in fd:
                line = comment_re.sub('', line).strip()
                if line != '':
                    tmp = whitespace_re.split(line, 1)
                    package = Atom(tmp[0])
                    tmp = whitespace_re.split(tmp[1])
                    flags = [ UseFlag(flag) for flag in tmp ]
                    self.packages.append((package, flags))
        finally:
            fd.close()

    def _cmpflag(self, lt, rt):
        return cmp(lt.name.lower(), rt.name.lower())

    def save(self, file=None):
        if file is None:
            if self.file is None:
                raise Exception('File not specified.')
            file = self.file
        maxlen = 0
        lines = []
        fd = open(file, 'w')
        try:
            items = _sortpkgs(self.packages)
            for item in items:
                flags = sorted(item[1], self._cmpflag)
                flagstr = ' '.join([ str(flag) for flag in flags ])
                line = (str(item[0]), flagstr)
                n = len(line[0])
                if n > maxlen:
                    maxlen = n
                lines.append(line)
            t = Tabber(maxlen)
            for line in lines:
                fd.write(t.pad(line[0]) + line[1] + '\n')
            fd.write('\n')
        finally:
            fd.close()

    def _findflag(self, flags, useflag):
        for i in range(len(flags)):
            if flags[i].match(useflag, use_enabled=False):
                return i
        return None

    def _norm(self, atom, flags):
        if not isinstance(atom, Atom):
            atom = Atom(atom)
        if flags is None:
            flags = []
        else:
            for i in range(len(flags)):
                if not isinstance(flags[i], UseFlag):
                    flags[i] = UseFlag(flags[i])
        return (atom, flags)

    def update(self, atom, flags):
        atom, flags = self._norm(atom, flags)
        npkg = _findpkg(self.packages, atom)
        if len(flags) > 0:
            if npkg is None:
                self.packages.append((atom, flags))
            else:
                for flag in flags:
                    nflag = self._findflag(self.packages[npkg][1], flag)
                    if nflag is None:
                        self.packages[npkg][1].append(flag)
                    else:
                        self.packages[npkg][1][nflag].enabled = flag.enabled

    def remove(self, atom, flags=None):
        atom, flags = self._norm(atom, flags)
        npkg = _findpkg(self.packages, atom)
        if npkg is not None:
            if len(flags) == 0:
                del self.packages[npkg]
            else:
                for flag in flags:
                    nflag = self._findflag(self.packages[npkg][1], flag)
                    if nflag is not None:
                        del self.packages[npkg][1][nflag]
                if len(self.packages[npkg][1]) == 0:
                    del self.packages[npkg]

    def packages(self):
        return [ item[0] for item in self.packages ]

    def flags(self, atom):
        if isinstance(atom, Atom):
            atom = Atom(atom)
        npkg = _findpkg(self.packages, atom)
        if npkg is None:
            return []
        else:
            return self.packages[npkg][1]

class Keywords:

    def __init__(self, file=None):
        self.packages = []
        self.file = file
        if file is not None:
            self._readfile(file)

    def _readfile(self, file):
        fd = open(file, 'r')
        try:
            for line in fd:
                line = comment_re.sub('', line).strip()
                if line != '':
                    tmp = whitespace_re.split(line, 1)
                    package = Atom(tmp[0])
                    keywords = []
                    if len(tmp) == 2:
                        tmp = whitespace_re.split(tmp[1])
                        keywords = [ Keyword(kw) for kw in tmp ]
                    self.packages.append((package, keywords))
        finally:
            fd.close()

    def _cmpkw(self, lt, rt):
        return cmp(str(lt), str(rt))

    def save(self, file=None):
        if file is None:
            if self.file is None:
                raise Exception('File not specified.')
            file = self.file
        maxlen = 0
        lines = []
        fd = open(file, 'w')
        try:
            items = _sortpkgs(self.packages)
            for item in items:
                if len(item[1]) == 0:
                    line = (str(item[0]), '')
                else:
                    kws = sorted(item[1], self._cmpkw)
                    kwstr = ' '.join([ str(kw) for kw in kws ])
                    line = (str(item[0]), kwstr)
                n = len(line[0])
                if n > maxlen:
                    maxlen = n
                lines.append(line)
            t = Tabber(maxlen)
            for line in lines:
                if line[1] == '':
                    fd.write(line[0] + '\n')
                else:
                    fd.write(t.pad(line[0]) + line[1] + '\n')
            fd.write('\n')
        finally:
            fd.close()

    def _norm(self, atom, keywords):
        if not isinstance(atom, Atom):
            atom = Atom(atom)
        if keywords is None:
            keywords = []
        else:
            for i in range(len(keywords)):
                if not isinstance(keywords[i], Keyword):
                    keywords[i] = Keyword(keywords[i])
        return (atom, keywords)

    def _findkw(self, keywords, keyword):
        for i in range(len(keywords)):
            if keywords[i].match(keyword):
                return i
        return None

    def add(self, atom, keywords=None):
        atom, keywords = self._norm(atom, keywords)
        npkg = _findpkg(self.packages, atom)
        if npkg is None:
            self.packages.append((atom, keywords))
        else:
            for keyword in keywords:
                nkw = self._findkw(self.packages[npkg][1], keyword)
                if nkw is None:
                    self.packages[npkg][1].append(keyword)

    def remove(self, atom, keywords=None):
        atom, keywords = self._norm(atom, keywords)
        npkg = _findpkg(self.packages, atom)
        if npkg is not None:
            if len(keywords) == 0:
                del self.packages[npkg]
            else:
                for keyword in keywords:
                    nkw = self._findkw(self.packages[npkg][1], keyword)
                    if nkw is not None:
                        del self.packages[npkg][1][nkw]

class Mask:

    def __init__(self, file=None):
        self.packages = []
        self.file = file
        if file is not None:
            self._readfile(file)

    def _readfile(self, file):
        fd = open(file, 'r')
        try:
            for line in fd:
                line = comment_re.sub('', line).strip()
                if line != '':
                    package = Atom(line)
                    self.packages.append((package,))
        finally:
            fd.close()

    def save(self, file=None):
        if file is None:
            if self.file is None:
                raise Exception('File not specified.')
            file = self.file
        fd = open(file, 'w')
        try:
            items = _sortpkgs(self.packages)
            for item in items:
                fd.write(str(item[0]) + '\n')
            fd.write('\n')
        finally:
            fd.close()

    def _norm(self, atom):
        if not isinstance(atom, Atom):
            atom = Atom(atom)
        return atom

    def add(self, atom):
        atom = self._norm(atom)
        npkg = _findpkg(self.packages, atom)
        if npkg is None:
            self.packages.append((atom,))

    def remove(self, atom):
        atom = self._norm(atom)
        npkg = _findpkg(self.packages, atom)
        if npkg is not None:
            del self.packages[npkg]

class Unmask(Mask):
    pass

